package com.netease.nie.base.mybatis.interceptor;

import java.sql.Connection;
import java.util.Properties;

import org.apache.commons.lang.StringUtils;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;

import com.netease.nie.base.mybatis.common.ShardTable;
import com.netease.nie.base.mybatis.sql.SqlParser;

/**
 * 
 * @author KingsHunter
 * @createDate July 11th,2016
 * 
 */
@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) })
public class TableShardInterceptor implements Interceptor {

	private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
	
	private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
	
	private static final ReflectorFactory DEFAULT_REFLECTOR_FACTORY = new DefaultReflectorFactory();
	
	public Object intercept(Invocation invocation) throws Throwable {
		StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
		MetaObject metaObject = MetaObject.forObject(statementHandler, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, DEFAULT_REFLECTOR_FACTORY);
		String originSql = (String) metaObject.getValue("delegate.boundSql.sql");
		BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
		if (StringUtils.isNotBlank(originSql)) {
			MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
			String id = mappedStatement.getId();
			String className = null;
			if (id.lastIndexOf(".") > -1)
				className = id.substring(0, id.lastIndexOf("."));
			else 
				className = id;
			Class<?> clazz = Class.forName(className);
			ShardTable shard = clazz.getAnnotation(ShardTable.class);
			if (shard != null) {
				SqlParser sqlParser = new SqlParser(mappedStatement, boundSql);
				String newSql = sqlParser.getActualSql(originSql, shard);
				if (StringUtils.isNotBlank(newSql))
					metaObject.setValue("delegate.boundSql.sql", newSql);
			}
		}
		return invocation.proceed();
	}

	public Object plugin(Object target) {
		if (target instanceof StatementHandler) {
			return Plugin.wrap(target, this);
		}
		return target;
	}

	public void setProperties(Properties properties) {
	}

}
